#!/bin/bash
#THIS DRIVER WRITTEN BY MATTHEW VONA ARB FOR THE BAM1020 MET
#WHEN RUNNING A QUERY PORT. THIS WAS TESTED USING FEM10 BAM1020 V3.6.3
#BAR CODE 20112138; 
# FOR QUESTIONS WRITE mailto:mvona@arb.ca.gov
#UPDATE: 04/17/2013; Changed to set the time at 23:55 instead of 07:54   
#-------------------------------------------------------------
#DISPLAY FILE CREATED
disp_created="0"
#THIS IS THE RELATIVE DIRECTORY VARIABLE 
DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# THIS IS THE FUNCTION CALLED IN THE EVENT THAT CTRL+C IS PRESSED
cleanup ()
{
	echo "Caught Signal ... cleaning up."
	if !(rm -f Data/.locked > /dev/null); then
 		exit 1
	fi
	for i in `pgrep -P ${plotterid}`; do
		kill -9 ${i}
	done
	kill -9 ${plotterid}	
	echo "Done cleanup ... quitting."
	exit 1
}
#########################################################################################
#						BEGIN MAIN DISPLAY FUNCTION.									#
#########################################################################################
###############################################################
# BEGIN DISPLAY FUNCTION : MJV 11/15/2013                     #
###############################################################
#THIS IS THE RELATIVE DIRECTORY VARIABLE 
DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
################################################################################
#########
#                       BEGIN MAIN DISPLAY FUNCTION.                            
##########################################################################################
#read config file
while read line; do
eval $line
done < Config/config
if [ -z $no_plots]; then
	bash ${DIR}/plotter.sh 1>/dev/null 2>/dev/null &
	plotterid="$!"
fi
        			
checkandset ()
{
	thishour=`date "+%H"`
	lasthour=`date -d "last hour" "+%H"`
	thisalert="errors$thishour"		
	#THE FOLLOWING VARIABLE DETERMINES WHAT THE DATE STAMP SHOULD BE
	#13 SECONDS FROM NOW.
	#WE NEED TO PASS A SECONDS STRING TO NEWER BAMS EVERY HOUR		
	set_time=`date -d "13 seconds" "+%H:%M:%S"`
	sjinn -b${bd_rt} -d ${tim_prt} -s "\r\r\r\r\8\pt\p1234\r\p${set_time}\r" -r$rsp_lngth -w$wt_rsp1 $p8n1 $dtr $ctr
}

#############################################################################################
# BEGIN MAIN BAM ERROR EXPANSION FUNCTION:MJV 6/17/2011
############################################################################################
expand ()
{
	#INITIALIZE THE INPUT VALUE
	input=$1
	#INITIALIZE THE RESULT
	result=''
	#INITIALIZE THE REMAINDER
	remainder='0'
	#INITIALIZE PARSED CODES
	parsedcodes=''
	#initialize the errors
	errors=''
	for i in 2048 1024 512 256 128 64 32 16 8 4 2 1; do
		remainder=`expr $input - $i`
		#echo "$input - $i = $remainder"
		if [[ $remainder -gt 0 ]]; then
			result="$result $i"
			input=$remainder  
			#echo "input is now $input" 
		elif [[ $remainder -lt 0 ]]; then
			false
			#echo $result
		elif [[ $remainder -eq 0 ]]; then
			result="$result $i"
			echo "$result"
			exit 0
		fi
	done
}
##############################################################################################
# BEGIN MAIN BAM ERROR TRANSLATION FUNCTION:MJV 6/17/2011
##############################################################################################
translate () {
	#initialize the variables
	error_string=''
	errors=''
	
	for c in $@; do
		case $c in
		2048)
			errors="EXTERNAL RESET ERROR, $errors"
			;;
		1024)
			errors="TELEMETRY FAULT, $errors"
			;;
		512)
			errors="MAINTENANCE FLAG, $errors"
			;;
		256)
			#IGNORE THIS ERROR SINCE WE ARE RUNNING IN COARS MODE WITHOUT A COARSE LINK
			:
			#errors="INTERNAL ERROR, COARSE LINK DOWN $errors"
			;;
		128)
			errors="POWER FAILURE, $errors"
			;;		
		64)
			errors="REFERENCE ERROR, $errors"
			;;
		32)
			errors="NOZZLE ERROR, $errors"
			;;
		16)
			errors="FLOW ERRORS, $errors"
			;;
		8)
			errors="PRESSURE DROP ALARM, $errors"
			;;
		4)
			errors="DEVIANT MEMBRANE ERROR, $errors"
			;;
		2)
			errors="BETA COUNT ERROR, $errors"
			;;
		1)
			errors="TAPE SYSTEM ERRORS, $errors"
			;;
		
		esac
	
	done	
	echo $errors | sed 's/,$//g'
}
############################################################################################
#BEGIN INTERPRET_ERROR FUNCTION
#THIS FUNCTION TAKES A NUMERICAL BAM ERROR ARGUEMENT
#AND RETURNS (VIA ECHO IN DEBUG) THE ENGLISH ERRORS
#BY SETTING A VARIABLE $error_string
############################################################################################ 
interpret_error () {
	this_expansion=`expand $@` 
	error_string=`translate "$this_expansion"`
	#DEBUG
	echo $error_string
}

rprt_nm= ; dvc_prt= ; bd_rt= ; cmnd1="\h1B5143480D\h\p\h1B51430D\p" ; wt_rsp1= ; bd_rt= ; rsp_lngth= ; izs_val= ;
dvc_prt= ; frqncy= ; dtr= ; ctr= ; p8n1= ; izs= ; saroad= ; p8n1= ; id_nmbr= ; factor="1000" ; tim_prt= ; 
instance=1 ; min_val=4 ;
##INITIALIZE THE BUFFER
return='NOT WRITTEN TO'
##loop through all variables and parameters
##Note that the position of the colon indicates the presence
##of a parameter to collect. The h parameter simply
##returns the help message.

while getopts “hr:m:s:d:b:c:w:l:t:C:p:I:T:” OPTION
do
case $OPTION in
	I)
	instance=$OPTARG
	;;
	s)
	saroad=$OPTARG
	;;
	r)
	rprt_nm=$OPTARG
	;;
	d)
	dvc_prt=$OPTARG
	;;
	b)
	bd_rt=$OPTARG
	;;
	c)
	cmnd1=$OPTARG
	;;
	w)
	wt_rsp1=$OPTARG
	;;
	l)
	rsp_lngth=$OPTARG
	;;
	t)
	dtr_val=$OPTARG
	;;
	T)
	tim_prt=$OPTARG
	;;
	C)
	ctr_val=$OPTARG
	;;
	p)
	p8n1_val=$OPTARG
	;;
	M)
	factor=$OPTARG
	;;
	m)
	min_val=$OPTARG
	;;
	h)
	help_me
	exit 1
	;;
	*)
usage
exit 1
;;
esac
done

#THE DIAGNOSTIC DATA (DIAG) ROUTINE TAKES THE DATA STREAM FROM THE API 400E
#AND TESTS FOR THE RECOGNITION OF KEY WORDS IN EACH LINE OF OUTPUT.
#IF THE KEY WORD IS DETECTED ON THAT LINE, A TEST IS PERFORMED. 
#IF THE TEST SUGGESTS THAT AN ERROR CONDITION EXISTS BASED UPON THE ALLOWABLE
#RANGES SPECIFIED BY THE ARB CHECKSHEET, THE OFFENDING LINE  IS WRITTEN OUT 
#TO A FILE IN THE Data DIRECTORY. THE NAME OF THE DESTINATION FILE IS
#errors{HH}. THE FILES CAN THEN BE PARSED AND UTILIZED BY ANY ALERT PROGRAM?DAEMON.
this_name=$(cat ${DIR}/.usr$instance | grep -o "\-r [A-Za-z0-9]*" | awk '{ print $2 }')
thishour=`date "+%H"`
thisalert="errors$thishour"
#THE DIAGNOSTIC DATA (DIAG) ROUTINE TAKES THE DATA STREAM FROM THE BAM_1020_BX965
#TO A FILE IN THE Data DIRECTORY. THE NAME OF THE DESTINATION FILE IS
#errors{HH}. THE FILES CAN THEN BE PARSED AND UTILIZED BY ANY ALERT PROGRAM?DAEMON.
diag()
{
	#this_name=$(cat resources/BAM1020_drv/.usr${instance} | grep -o "\-r [-A-Za-z0-9]*" | awk '{ print $2 }')
	thishour=`date "+%H"`
	lasthour=`date -d "last hour" "+%H"`
	thisalert="errors$thishour"
	if [ -e ${DIR}/.${lasthour}_${instance}_bam_config ]; then
		diff_count=$(diff "${DIR}/.${lasthour}_${instance}_bam_config" "${DIR}/.${thishour}_${instance}_bam_config" | wc -l)
		if [ "${diff_count}" -gt "8" ]; then
			echo "$rprt_nm configuration changed from ${lasthour} to ${thishour}: " > ${DIR}/.bamcfgdlta_${instance}_${thishour} 
			echo "$rprt_nm configuration at ${lasthour} : " >> ${DIR}/.bamcfgdlta_${instance}_${thishour}
			echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=" >> ${DIR}/.bamcfgdlta_${instance}_${thishour} 
			cat "${DIR}/.${lasthour}_${instance}_bam_config" | awk 'NF>0 { print }' >> ${DIR}/.bamcfgdlta_${instance}_${thishour}
			echo "$rprt_nm configuration at ${thishour} : " >> ${DIR}/.bamcfgdlta_${instance}_${thishour}
			echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=" >> ${DIR}/.bamcfgdlta_${instance}_${thishour}
			cat "${DIR}/.${thishour}_${instance}_bam_config" | awk 'NF>0 { print }' >> ${DIR}/.bamcfgdlta_${instance}_${thishour}
		fi
	fi
}

#BEGIN WRITE
#THIS FUNCTION HANDELS READ RIGHT HANDSHAKES BETWEEN VARIOUS DRIVERS
#IN GENERAL, THE DATA IS PLACED IN THE VARIABLE $TIMESTAMP PRIOR TO CALL
#NEXT, IT OPENS THE DATA DIRECTORY AND LOOKS FOR THE .locked FILE
#IF IT EXISTS, IT HANGS OUT UNTIL IT DOESN'T EXIST. AT THAT POINT
#IT CREATES A .locked FILE, AND APPENDS TO THE DATA RECORD.
#FINALLY IT REMOVES THE .locked FILE IT HAS CREATED.
#THIS FUNCTION ALSO PARSES THE .DISABLED FILE IN THE DRIVER FOLDER
#AND TAKES A CHANNEL OFF LINE BY PREPENDING --DISABLED-- TO EACH LINE OF CONTENT IN THE DATA DIRECTORY
#written by MJV
write()
{
	#CONFIRM THAT THERE IS NO HIDDEN FILE CALLED '.locked'
	while [ -e "Data/.locked" ]; do
			sleep .2
	done	
	echo "LOCKED" > "Data/.locked"
	#PARSE THE .disabled File
	channel_status=`cat "${DIR}/.disabled${instance}"`
	#run display
	#display
	#IF THE FILE DOSEN'T EXIT OR IS NOT ACCESIBLE
	#MAKES SURE A VALUE OF 0 IS USED

	source sys_resources/translate_code.sh displaycode $channel_status rawcode
	
	if [ "$error_flag" -eq "1" ]; then
		echo "${timestamp}${rprt_nm}${occurrence} null detected...maybe the CARBLogger was unplugged... " >> "Data/$thisalert"
		echo "${timestamp}${rprt_nm}${occurrence} may be disconnected PM10_${instance}=999 PM10s_${instance}=999 $target_value" >> "Data/${datafile}"
	else	
		if !(echo "$data" | grep -v '<Esc>' |sed 's/^/'$timestamp'/g'| sed 's/$/'$target_value'/g' | tee -a "Data/$datafile"); then
			echo "$PWD $0 GENERAL FAILURE WRITING TO RECORD: $#"
			cleanup
		fi
	fi

	if !(rm -f "Data/.locked" > /dev/null); then
		interpret_error	echo "GENERAL FAILURE REMOVING LOCK: $#"
		exit 1
	fi
	if !(timestamp="" && data=""); then
		echo "GENERAL FAILURE CLEARING WRITE BUFFER: $#"
		cleanup
	fi	
}

#THIS IS THE ACTUAL COMMUNICATION WITH THE INSTRUMENT
#THE TIMER CONTROL. AT THE BEGINING IT TESTS FOR THE 
#EXISTENCE IF TOGGLE VARRIABLES WHICH ARE UTILIZED BOTH BY
#THE COMM ROUTINE runit() AND THE DIAGNOSTICS DATA ROUTINE
#diag()
runit()
{
	#IF THE -d, -C, or -p variable is set to on, then append those parameters
	#to the sjinn communication string. If the -i parameter is passed, diag will
	#test for IZS specific routines.
	if [ "$dtr_val" = "on" ]; then
	 	dtr="--dtr" 
	fi
	if [ "$ctr_val" = "on" ]; then
	 	ctr="--ctr"
	fi
	if [ "$p8n1_val" = "on" ]; then
	 	p8n1="-p8n1"
	fi
	#Date stamp
	today=`date "+%Y-%d-%m"`
	timestamp=$rprt_nm::`date "+%m-%d-%Y_%H:%M"`:::
	#THIS IS THE NAME OF THE DATA FILE RIGHT NOW
	datafile="$saroad-$today"

	##################################################################################################
	#
	#BEGIN BAM1020 PM10S and PM10 COM ROUTINE:
	#THIS ROUTINE DESIGNED FOR CONNECTION TO "QUERY" PORT ON BAM 1020
	#IT MUST FIRST USE <ESC>QCH TO DETERMINE THE CONFIGURATION OF THE BAM
	#AND SUBSEQUENTLY SEND A QC TO EXTRACT THE INFORMATION
	#$data=the composite of the header and the data
	#$dch=the header
	#$dcc=the data
	#NOTE THAT IN ORDER FOR THIS TO WORK HAD TO REMOVE THE DATA AND TIME STAMP SINCE THEY WERE SPACE SEPERATED,
	#AND RECOMBINE AS A SINGLE BAM_TIME ENTRY.
	##################################################################################################

	dch=""
	dcc=""
	loop_count="1"
	error_flag=0
	while [ "$loop_count" -le "5" ]; do
		garbage=`sjinn -b$bd_rt -d $dvc_prt -s "\h0D0D0D" -r$rsp_lngth -w1 $p8n1 $dtr $ctr`
		dch=`sjinn -b$bd_rt -d $dvc_prt -s "\h1B5143480D" -r$rsp_lngth -w$wt_rsp1 $p8n1 $dtr $ctr\
| grep -v "^$" | sed 's/(mg\/m3)//g' | sed 's/(m3)//g' | sed 's/(mg)//g' | sed 's/<Esc>QCH//g' | sed 's/(mmHg)//g' |\
sed 's/(C)//g' | sed 's/(KTS)//g' | sed 's/(DEG)//g' | sed 's/(lpm)//g' | sed 's/(%)//g' | sed 's/,/ /g' | sed 's/\*[0-9]*//g' | sed 's/TIME//g' `
		#wait $!
		header_check=`echo "$dch" | grep "PM10" | grep "PM10s" | grep "Q_" | wc -l`
		header_count=`echo "$dch" | sed 's/,/ /g' | wc -w`		
		if [ "$header_check" -gt "0" ]; then
			loop_count="6"		
		else
			loop_count=`expr $loop_count + 1`
			if [ "$loop_count" -eq "6" ]; then
				error_flag=1
			fi
		fi			
		sleep 30
	done
	loop_count="1"			
	
	while [ "$loop_count" -le "5" ]; do	
		garbage=`sjinn -b$bd_rt -d $dvc_prt -s "\h0D0D0D" -r$rsp_lngth -w1 $p8n1 $dtr $ctr`
		dcc=`sjinn -b$bd_rt -d $dvc_prt -s "\h1B51430D" -r$rsp_lngth -w$wt_rsp1 $p8n1 $dtr $ctr\
| grep -v "^$" | sed 's/(mg\/m3)//g' | sed 's/<Esc>QC//g' | sed 's/,/ /g'`
		#wait $!
		data_check=`echo "$dcc" | wc -w`
		header_count_temp=`expr $header_count + 3`
		date=`echo $dcc |awk '{ for(i=1; i<=NF; i++) if($i ~/[0-9][0-9]\/[0-9][0-9]\/[0-9][0-9]/) {print $i}}'` 
		ctime=`echo $dcc |awk '{ for(i=1; i<=NF; i++) if($i ~/[0-9][0-9]\:[0-9][0-9]/) {print $i}}'`
		if [ "$data_check" -eq "$header_count_temp" ]; then
			loop_count="6"
		else
			loop_count=`expr $loop_count + 1`
			if [ "$loop_count" -eq "6" ]; then
				error_flag=1
			fi
		fi
		sleep 30
	done
	
	###################################################################################################
	#
	#BEGIN THE TIME CHECKING AND SETTING ROUTINE
	#
	###################################################################################################
	dcc=`echo $dcc | sed 's/[0-9][0-9]:[0-9][0-9]//g' | sed 's/[0-9][0-9]\/[0-9][0-9]\/[0-9][0-9]//g'`
	data=''
	COUNTER=1
	for i in `echo ${dch}`; do
		data_value=`echo ${dcc} | awk -v position="${COUNTER}" '{ print $position }'`
		#FOR CARB, WE NEED TO MULTIPLY THE BAM REPORTED VALUES BY 1000 IN ORDER TO STORE ug/m3
		#THIS SECTION TESTS FOR THE PRESENCE OF ANY 4 POSSIBLE PMS VALUES
		#AND (IF FOUND) MULTIPLYS THEM BY 1000
		if [ "PM2.5" == "${i}" ] || [ "PM10" == "${i}" ] || [ "PM10s" == "${i}" ] || [ "PMc" == "${i}" ] || [ "CONC" == "${i}" ]; then
			eval data_value=`echo "${data_value}*${factor}" | bc`;

		elif [ -z "$(echo ${i} | tr -d '.')" ]; then	
			:	
		else
			eval "${i}=${data_value}"	
		fi
		COUNTER=`echo "${COUNTER}+1" | bc`
		if [ "$instance" > "1" ]; then
			data="$data ${i}_${instance}=${data_value}"	
		else
			data="$data ${i}=${data_value}"
		fi
	done
	data="${data} BAM_TIME ${date} ${ctime}"
	this_error=`interpret_error ${ERRORS}`
	#######REPORT ERRORS
	this_hour=`date '+%H'`
	if [[ -n "$this_error" ]]; then
		echo `date`"${rprt_nm} has experienced:${this_error}" >> Data/errors${this_hour}  
	fi

	data="$data ${this_error}"
	data=`echo $data | sed 's/\.\.\.//g' | sed 's/\*[0-9]*//g'`
	echo "${data}"
	write
}
#THIS IS THE FUNCTION RUN IN THE EVENT THAT THE REQUIRED PARAMETERS
#ARE NOT ENTERED.
usage()
{
	clear| grep -v '<esc>' 
	cat << EOF
usage: $0 options
OPTIONS:
-h	display help
-n	the device identifier
-r	rprt_nm [rn]
-d	dvc_prt [eg, /dev/ttyS0, /dev/ttyUSB1] 
-b	bd_rt [2400,4800,9600,etc.]
-c	cmnd [default is cmnd1="\h1B51480D\h\p\h1B510D\p"]
-w	wt_rsp1 [seconds]
-l	rsp_lngth [characters]
-s	SAROAD number of the station
-t	[on/off] (OPTIONAL)
-C	[on/off] (OPTIONAL)
-p	[on/off] (OPTIONAL)
-m  Minute to query the BAM on.
-M	Factor to multiply the BAM1020 PM values by [DEFAULT is 1000]
-I	instance
-T	time port, this is the serial port used to set the bam time.
Last updated 10/3/2011
EOF
}

#HELP FUNCTION GIVES BACKGROUND ON THIS DRIVER
help_me ()
{
	clear
	cat .help
	exit 0
}
def_opts ()
{
	clear
	cat .options
}

##CLEAR ALL VARIABLES AND SET DEFAULTS               

#CHECK FOR THE CRITICAL PARAMETERS, IF THEY ARE NOT PRESENT THEN
#RUN THE USAGE FUNCTION WHICH RETURNS THE INSTRUCTIONS FOR PROPER USAGE
#MUST HAVE r n d b c w l i s
if [ -z "$rprt_nm" ] || [ -z "$dvc_prt" ] || [ -z "$bd_rt" ] || [ -z "$cmnd1" ] ||
[ -z "$wt_rsp1" ] || [ -z "$rsp_lngth" ] || [ -z "$saroad" ] || [ -z "$tim_prt" ] || [ -z "$min_val" ]
	then
		usage
		exit
fi
#INITIALIZED THE RAN VARIABLE. THIS IS USED TO TOGGLE THE TIMER GATE
ran=0

#TRAP CTRL+C
trap cleanup 1 2 3 6 15
#SETUP THE OPERATING LOOP
while :; do
	hour=`date "+%H"`
	second=`date "+%S"`
	minute=`date "+%M"`
	if [ "$second" -eq "00" ] && [ "$ran" -eq "0" ] && [ "$minute" -eq "$min_val" ]; then
		#RUN THE RUNIT FUNCTION
		runit
		#TOGGLE THE RAN VARIABLE SO THAT THE PROCESS WILL NOT REPEAT
		#UNTIL THE BEGINNING OF THE NEXT MINUTE.
		ran=1
		#WAIT FOR COMPLETION OF THE RUNIT FUNCTION AND THE VARIABLE
		#SET
		sleep 1
	elif [ "$second" -eq "00" ] && [ "$minute" -eq "55" ] && [ "$hour" -eq "23" ]; then
		#run the time setting and checking routine
		checkandset
		sleep 60
	else 
		#WAIT ONE SECOND
		sleep .5
		#SET RAN TO 0
		ran=0
	fi
done
exit

